<?php

App::uses('AppModel', 'Model');
App::uses('CompositeKeyModel', 'Model');

/**
 * OrganisationAccount Model
 *
 * @property Organisation $Organisation
 */
class OrganisationAccount extends CompositeKeyModel {

    //The Associations below have been created with all possible keys, those that are not needed can be removed

    public $useDbConfig = "organisations";
    public $primaryKeys = array('id', 'organisation_id');

    /**
     * belongsTo associations
     *
     * @var array
     */
    public $belongsTo = array(
        'Organisation' => array(
            'className' => 'Organisation',
            'foreignKey' => 'organisation_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        ),
        'OrganisationRole' => array(
            'className' => 'OrganisationRole',
            'foreignKey' => 'organisation_role_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        ),
        'MemberAccount' => array(
            'className' => 'MemberAccount',
            'foreignKey' => 'member_account_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        ),
    );

    /**
     * Verifies and returns information about an organisation account when a login
     * attempt is made.
     * 
     * @param int $username
     * @param int $password
     * @return array|boolean
     * @deprecated
     */
    public function attempt_login($username, $password) {
        $accounts = $this->find('all', array(
            'contain' => array(
                'Organisation' => array(
                    'OrganisationSubscription' => array(
                        'limit' => 1,
                        'order' => array('start_dt' => 'desc')
                    ),
                    'Module' => array(
                        'ModuleMenu' => array(
                            'order' => array('ModuleMenu.position'),
                            'conditions' => array(
                                'ModuleMenu.active' => 1
                            )
                        )
                    )
                )
            ),
            'conditions' => array(
                'OrganisationAccount.username' => $username,
                'OrganisationAccount.active' => 1,
                'OrganisationAccount.deleted' => 0,
                'Organisation.active' => 1 // allow login only if organisation is active
            )
        ));

        foreach ($accounts as $account) {
            if ($account['OrganisationAccount']['password'] == md5($password . $account['OrganisationAccount']['pass_salt'])) {
                // todo other things
                return $account;
            }
        }

        return false;
    }
    
    /**
     * Verifies and returns information about an organisation account when a login
     * attempt is made.
     * 
     * @param int $organisation_id
     * @param int $member_account_id
     * @return array|boolean
     */
    public function manage($organisation_id, $member_account_id) {
        $account = $this->find('first', array(
            'contain' => array(
                'Organisation' => array(
                    'OrganisationSubscription' => array(
                        'limit' => 1,
                        'order' => array('start_dt' => 'desc')
                    )
                ),
                'OrganisationRole'
            ),
            'conditions' => array(
                'OR' => array(
                    'OrganisationAccount.organisation_id' => $organisation_id,
                    'Organisation.slug' => $organisation_id
                ), 
                'OrganisationAccount.member_account_id' => $member_account_id,
                'OrganisationAccount.active' => 1,
                'OrganisationAccount.deleted' => 0,
                'Organisation.active' => 1 // allow login only if organisation is active
            )
        ));
        
        $role_modules = ClassRegistry::init('OrganisationRoleModule')->find('all', array(
            'conditions' => array(
                'OrganisationRoleModule.organisation_role_id' => $account['OrganisationAccount']['organisation_role_id'],
            ),
            'recursive' => -1
        ));
        
        if( $role_modules ) {
            $module_ids = Hash::extract($role_modules, '{n}.OrganisationRoleModule.module_id');
            $module_menu_id_data = Hash::extract($role_modules, '{n}.OrganisationRoleModule.module_menu_ids');
            $module_menu_ids = array();
            
            foreach($module_menu_id_data as $mid) {
                $module_menu_ids = array_merge($module_menu_ids, explode(",", $mid));
            }
            
            $modules = ClassRegistry::init('Module')->find('all', array(
                'contain' => array(
                    'ModuleMenu' => array(
                        'order' => array('ModuleMenu.position'),
                        'conditions' => array(
                            'ModuleMenu.active' => 1,
                            'ModuleMenu.id' => $module_menu_ids
                        )
                    )
                ),
                'conditions' => array(
                    'Module.id' => $module_ids
                ),
                'order' => array('Module.menu_position')
            ));
            
            $final = array('Module' => array());
            
            foreach($modules as $mod) {
                
                $mod['Module']['ModuleMenu'] = $mod['ModuleMenu'];
                unset($mod['ModuleMenu']);
                
                $final['Module'][] = $mod['Module'];
            }
            
            $account['Organisation'] = array_merge($account['Organisation'], $final);
        }
        
        return $account;
    }

    /**
     * Returns a list of all accounts in a given organisation
     * 
     * @param int $organisation_id
     * @return array
     */
    public function getOrganisationAccounts($organisation_id) {
        return $this->find('all', array(
            'contain' => array(
                'MemberAccount' => array(
                    'Member' => array(
                        'fields' => array('id', 'first_name', 'last_name', 'title', 'full_name'),
                        'MemberImage' => array(
                            'limit' => 1,
                            'order' => array('MemberImage.id' => 'desc')
                        )
                    )
                ),
                'OrganisationRole'
            ),
            'conditions' => array(
                'OrganisationAccount.organisation_id' => $organisation_id,
                'OrganisationAccount.deleted' => 0
            )
        ));
    }
    
    /**
     * Returns information about a specific account for a given organisation
     * 
     * @param int $organisation_id
     * @param int $account_id
     * @return array
     */
    public function getOrganisationAccount($organisation_id, $account_id) {
        $account = $this->find('first', array(
            'contain' => array('Organisation', 'OrganisationRole', 'OrganisationMember' => array('Member', 'MemberImage')),
            'conditions' => array(
                'OrganisationAccount.organisation_id' => $organisation_id,
                'OrganisationAccount.id' => $account_id,
                'OrganisationAccount.active' => 1,
                'OrganisationAccount.deleted' => 0
            )
        ));
        
        if( $account ) {
            return array('status' => 'success', 'account' => $account);
        }
        
        return array('status' => 'failed', 'message' => 'Account not found for supplied information.');
    }
    
        /**
     * Returns information about a specific account
     * 
     * @param int $account_id
     * @return array
     */
    public function getAccount($account_id) {
        return $this->find('first', array(
            'conditions' => array(
                'OrganisationAccount.id' => $account_id
            ),'recursive' => -1
        ));
    }
    
    /**
     * Returns information about a specific account
     * 
     * @param int $organisation_id
     * @return array
     */
    public function getDefaultAccount($organisation_id) {
        return $this->find('first', array(
            'conditions' => array(
                'OrganisationAccount.organisation_id' => $organisation_id
            ),
            'recursive' => -1
        ));
    }
    
    /**
     * 
     * @param type $organisation_id
     * @param type $account_data
     * @return type
     * @deprecated since version 0.2.5
     */
    public function _updateAccount($organisation_id, $account_data) {
        
        $availability = $this->checkAvailability($organisation_id, $account_data['OrganisationAccount'][0]['username']);
        
        // if creating new profile record
        if( empty($account_data['OrganisationAccount'][0]['id']) ) {
            
            // if not organisation account with supplied username exists
            if( $availability['status'] == 'available' ) {
                $next_num = ClassRegistry::init('OrganisationCounter')->getCount($organisation_id, 'member');
                $setting = ClassRegistry::init('OrganisationSetting')->getSetting($organisation_id, 'id_prefix');
                $prefix = $setting['OrganisationSetting']['value'];
                $password = $account_data['OrganisationAccount'][0]['password'];
                $pass_salt = rand(10000, 99999);

                $account_data['OrganisationAccount'][0] = array_merge($account_data['OrganisationAccount'][0], array(
                    'first_name' => $account_data['Member']['first_name'],
                    'last_name' => $account_data['Member']['last_name'],
                    'password' => md5($password . $pass_salt),
                    'pass_salt' => $pass_salt
                ));
                
                // if an existing membership for the supplied username exists for this organisation
                if( $availability['profile'] ) {
                    $account_data['Member'] = $availability['profile']['Member'];
                    $account_data['OrganisationMember'] = $availability['profile']['OrganisationMember'];
                }
                
                else {
                    $category = ClassRegistry::init('OrganisationMemberCategory')->getDefaultCategory($organisation_id);
                    
                    $account_data['OrganisationMember'] = array_merge($account_data['OrganisationMember'], array(
                        'organisation_no' => $prefix .  $next_num,
                        'organisation_member_category_id' => $category['OrganisationMemberCategory']['id'],
                        'status' => 'member',
                        'source' => 'admin',
                        'approved' => 1
                    ));
                    
                    $account_data['Member']['email'] = $account_data['OrganisationAccount'][0]['username'];
                    $account_data['Member']['source_organisation_id'] = $organisation_id;
                }
            }
            
            else {
                return array('status' => 'failed', 'message' => $availability['message']);
            }
        }
        
        else {
            $account_data['OrganisationAccount'][0] = array_merge($account_data['OrganisationAccount'][0], array(
                'first_name' => $account_data['Member']['first_name'],
                'last_name' => $account_data['Member']['last_name']
            ));

            $account_data['Member']['email'] = $account_data['OrganisationAccount'][0]['username'];
            $account_data['Member']['source_organisation_id'] = $organisation_id;
        }
        
        $OrganisationMember = ClassRegistry::init('OrganisationMember');
        $saved = $OrganisationMember->saveAll($account_data);
        
        if( $saved ) {
            $message = 'Account created successfully.';
            
            if( $availability['status'] == 'available' && isset($availability['profile']) ) {
                $message .= " Account linked to existing membership successfully";
            }
            
            return array('status' => 'success', 'message' => $message);
        }
        
        else {
            return array('status' => 'failed', 'message' => 'Account creation failed due to server side error. Please contact support');
        }
    }
    
    /**
     * Deactivates an organisation account with the account_id supplied for 
     * the organisation_id
     * 
     * @param type $organisation_id
     * @param type $account_id
     * @return type
     */
    public function deactivate($organisation_id, $account_id) {
        return $this->updateAll(array('OrganisationAccount.active' => 0), array('OrganisationAccount.organisation_id' => $organisation_id, 'OrganisationAccount.id' => $account_id));
    }
    
    /**
     * Moves an organisation account to the trash
     * the organisation_id
     * 
     * @param type $organisation_id
     * @param type $account_id
     * @return type
     */
    public function trash($organisation_id, $account_id) {
        return $this->updateAll(array('OrganisationAccount.deleted' => 1), array('OrganisationAccount.organisation_id' => $organisation_id, 'OrganisationAccount.id' => $account_id));
    }
    
    /**
     * 
     * @param type $organisation_id
     * @param type $username
     */
    public function checkAvailability($organisation_id, $username) {
        
        $account = $this->find('count', array(
            'conditions' => array(
                //'OrganisationAccount.organisation_id' => $organisation_id,
                'OrganisationAccount.username' => $username
            )
        ));
        
        if( $account ) {
            return array('status' => 'unavailable', 'message' => 'An account with supplied email/username already exists. Please choose a different username to continue.');
        }
        
        $member = null;
        $mObj = ClassRegistry::init('Member')->find('first', array(
            'conditions' => array('Member.email' => $username),
            'recursive' => -1
        ));
        
        if( $mObj ) {
            $member = ClassRegistry::init('OrganisationMember')->find('first', array(
                'contain' => array('Member'),
                'conditions' => array(
                    'OrganisationMember.organisation_id' => $organisation_id,
                    'OrganisationMember.member_id' => $mObj['Member']['id']
                )
            ));
        }
        
        if( $member ) {
            return array('status' => 'available', 'profile' => $member, 'message' => 'An existing membership exists for this username and the profile will be linked to this account');
        }
        
        return array('status' => 'available', 'profile' => false, 'message' => 'This username is available. Membership profile information required');
    }
    
    /**
     * Returns a row
     * 
     * @param int password
     * @return array
     */
    public function validatePass($pass,$account_id) {
        $account = $this->getAccount($account_id); 
        $original_pass = $account['OrganisationAccount']['password'];
        $entered_pass = md5($pass . $account['OrganisationAccount']['pass_salt']);
        $status = ($original_pass == $entered_pass) ? 'matched' : 'failed';
        return array('status' => $status);
    }
    
    /**
     * Activates an account if the validation passes
     * 
     * @param type $organisation_id
     * @param type $username
     * @param type $check_hash
     * @return type
     */
    public function activateAccount($organisation_id, $username, $check_hash) {
        
        $account = $this->find('first', array(
            'contain' => array(
                'MemberAccount' => array(
                    'Member' => array(
                        'fields' => array('first_name')
                    )
                )
            ),
            'conditions' => array( 
                'OrganisationAccount.organisation_id' => $organisation_id,
                'MemberAccount.username' => $username,
                'OrganisationAccount.active' => 0,
                'OrganisationAccount.deleted' => 0
            )
        ));
        
        if( $account ) {
            $validation_hash = sha1($account['OrganisationAccount']['organisation_id'] . $account['MemberAccount']['username'] . $account['MemberAccount']['Member']['first_name']);
            
            if( $validation_hash == $check_hash ) {
                $this->save(array(
                    'id' => $account['OrganisationAccount']['id'],
                    'organisation_id' => $account['OrganisationAccount']['organisation_id'],
                    'active' => 1
                ));
                
                if( $account['MemberAccount']['active'] == 0 ) {
                    $this->MemberAccount->save(array(
                        'id' => $account['MemberAccount']['id'],
                        'member_id' => $account['MemberAccount']['member_id'],
                        'username' => $account['MemberAccount']['username'],
                        'active' => 1
                    ));
                }
                
                // remove member account data and reload is manually
                unset($account['MemberAccount']);
                
                $member_account = $this->MemberAccount->find('first', array(
                    'contain' => array(
                        'Member' => array(
                            'limit' => 1,
                            'MemberImage' => array(
                                'order' => array('id' => 'desc'),
                                'limit' => 1
                            )
                        ),
                        'OrganisationAccount' => array('Organisation')
                    ),
                    'conditions' => array(
                        'MemberAccount.username' => $username,
                        'MemberAccount.active' => 1,
                        'MemberAccount.deleted' => 0
                    )
                ));
                
                unset($member_account['MemberAccount']['password']);
                unset($member_account['MemberAccount']['pass_salt']);
                
                return array(
                    'status' => 'success', 
                    'message' => 'Account activated', 
                    'member_account' => $member_account, 
                    'org_account' => $this->manage($account['OrganisationAccount']['organisation_id'], $member_account['MemberAccount']['id'])
                );
            }
            
            else {
                return array('status' => 'failed', 'message' => 'Account validation failed');
            }
        }
        
        else {
            return array('status' => 'failed', 'message' => 'Account not found');
        }
    }

    public function changePassword($organisation_id, $account_id, $old_pass, $new_pass) {
        $account = $this->find('first', array(
            'conditions' => array(
                'OrganisationAccount.organisation_id' => $organisation_id,
                'OrganisationAccount.id' => $account_id
            ),
            'recursive' => -1
        ));
        
        if( !$account ) {
            return array('status' => 'failed', 'message' => 'No account found for this process.');
        }
        
        $old_pass_hash = $account['OrganisationAccount']['password'];
        $check_hash = md5($old_pass . $account['OrganisationAccount']['pass_salt']);
        
        if( $old_pass_hash != $check_hash ) {
            return array('status' => 'failed', 'message' => 'Old password specified is incorrect. Please check and try again.', 
                'check_hash' => $check_hash, 'old_hash' => $old_pass_hash);
        }
        
        $new_pass_salt = rand(10000,99999);
        $new_pass_hash = md5($new_pass . $new_pass_salt);
        
        $saved = $this->save(array(
            'id' => $account_id,
            'organisation_id' => $organisation_id,
            'password' => $new_pass_hash,
            'pass_salt' => $new_pass_salt
        ));
        
        if( $saved ) {
            return array('status' => 'success', 'message' => 'Password change completed successfully.');
        }
        
        return array('status' => 'failed', 'message' => 'Password change process failed unexpectedly. Please try again later.');
    }
    
    public function lookupAccount($organisation_id, $term) {
        
        if( empty($term) ) {
            return array('status' => 'failed', 'message' => 'Please specify a search term');
        }
        
        $sql = sprintf("SELECT Member.id FROM `members` Member "
             . "LEFT JOIN `member_accounts` MemberAccount ON MemberAccount.member_id = Member.id AND MemberAccount.active = 1 "
             . "INNER JOIN `member_emails` MemberEmail ON MemberEmail.member_id = Member.id AND MemberEmail.active = 1 AND MemberEmail.email IS NOT NULL AND MemberEmail.email != '' "
             . "INNER JOIN `organisation_members` OrganisationMember ON OrganisationMember.member_id = Member.id "
             . "LEFT JOIN `organisation_accounts` OrganisationAccount ON OrganisationAccount.member_account_id = MemberAccount.id AND OrganisationAccount.active = 1 AND OrganisationAccount.organisation_id = %d "
             . "WHERE OrganisationAccount.id IS NULL AND OrganisationMember.organisation_id = %d  AND "
             . " (CONCAT(Member.first_name, ' ', Member.last_name) LIKE '%%%s%%' OR MemberEmail.email LIKE '%%%s%%' ) ",
             $organisation_id, $organisation_id, $term, $term);
        
        $result = $this->query($sql);
        
        if( $result ) {
            $ids = Hash::extract($result, '{n}.Member.id');
            
            $data = ClassRegistry::init('OrganisationMember')->find('all', array(
                'contain' => array(
                    'Member' => array(
                        'fields' => array('id', 'full_name'),
                        'MemberImage' => array(
                            'fields' => array('thumb_url'),
                            'order' => array('MemberImage.id' => 'desc'),
                            'limit' => 1
                        ),
                        'MemberAccount' => array(
                            'conditions' => array('MemberAccount.active' => 1)
                        ),
                        'MemberEmail' => array(
                            'limit' => 1
                        )
                    )
                ),
                //'fields' => array('organisation_no'),
                'conditions' => array(
                    'OrganisationMember.organisation_id' => $organisation_id,
                    'OrganisationMember.member_id' => $ids,
                    'OrganisationMember.active' => 1
                )
            ));
            
            $list = array();

            foreach($data as $r) {
                $list[] = array(
                    'member_id' => $r['Member']['id'],
                    'email' => isset($r['Member']['MemberEmail'][0]) ? $r['Member']['MemberEmail'][0]['email'] : null,
                    'label' => $r['Member']['full_name'] . " ({$r['OrganisationMember']['organisation_no']})",
                    'text' => $r['OrganisationMember']['organisation_no'] . ' - ' . $r['Member']['full_name'],
                    'thumb_url' => isset($r['Member']['MemberImage'][0]) ? $r['Member']['MemberImage'][0]['thumb_url'] : null
                        //,
                    //'sql' => $sql
                );
            }

            return array('status' => 'success', 'data' => $list, 'all' => $data);
        }
        
        return array('status' => 'failed', 'message' => 'No records found', 'result' => $result);
    }
    
    /**
     * Triggers the process of add a member account to an organisation. If the member specified
     * does not have an active account, the system will generate a temporary account and return
     * the account id. An email notification should be sent to the member to notify them of their
     * new account. They will be required to change their password once they activate the account
     * 
     * @param int $organisation_id
     * @param int $member_id
     * @param int $organisation_role_id
     * @return type
     */
    public function addAccount($organisation_id, $member_id, $organisation_role_id) {
        
        $temp_account_id = false;
        $member_account = $this->MemberAccount->find('first', array(
            'contain' => array(
                'Member' => array(
                    'fields' => array(
                        'id', 'first_name', 'last_name', 'dob', 'email'
                    )
                )
            ),
            'fields' => array('id', 'username', 'member_id', 'timezone', 'last_login', 'created', 'active'),
            'conditions' => array(
                'MemberAccount.member_id' => $member_id
            )
        ));
        
        
        if( !$member_account ) {
            $member_account = $this->MemberAccount->createTempAccount($member_id);
            $temp_account_id = $member_account['MemberAccount']['id'];
        }
        
        if( $member_account ) {
            $response = $this->_createOrgAccountRecord($organisation_id, $member_account['MemberAccount']['id'], $organisation_role_id);
            
            if( $temp_account_id ) {
                $response['temp_member_account_id'] = $temp_account_id;
            }
            
            return $response;
        }
        
        return array('status' => 'failed', 'code' => 'CA001', 'message' => 'Member does not have existing system login account and one could not be created');
    }
    
    /**
     * Creates the organisation account record
     * 
     * @param int $organisation_id
     * @param int $member_account_id
     * @param int $organisation_role_id
     * @return array
     */
    private function _createOrgAccountRecord($organisation_id, $member_account_id, $organisation_role_id) {
        $org_account = $this->find('first', array(
            'conditions' => array(
                'OrganisationAccount.organisation_id' => $organisation_id,
                'OrganisationAccount.member_account_id' => $member_account_id
            )
        ));

        if( !$org_account ) {
            $this->create();

            $saved = $this->save(array(
                'organisation_id' => $organisation_id,
                'member_account_id' => $member_account_id,
                'organisation_role_id' => $organisation_role_id,
                'active' => 0
            ));

            if( $saved ) {
                return array(
                    'status' => 'success', 'code' => 'CA004', 
                    'message' => 'Account successfully added to organisation', 
                    'organisation_account_id' => $this->id
                );
            }

            return array('status' => 'failed', 'code' => 'CA003', 'message' => 'Account save process failed. Please try again or contact support');
        }
        
        else {
            if( $org_account['OrganisationAccount']['active'] == 0 ) {
                $this->updateAll(array(
                    'OrganisationAccount.active' => 1,
                    'OrganisationAccount.organisation_role_id' => $organisation_role_id
                ), array(
                    'OrganisationAccount.id' => $org_account['OrganisationAccount']['id'],
                    'OrganisationAccount.organisation_id' => $org_account['OrganisationAccount']['organisation_id']
                ));
                
                return array(
                    'status' => 'success', 'code' => 'CA005', 
                    'message' => 'Existing account activated', 
                    'organisation_account_id' => $org_account['OrganisationAccount']['id']
                );
            }
        }

        return array('status' => 'failed', 'code' => 'CA002', 'message' => 'Account already exists');
    }
    
    /**
     * Changes the organisation account information
     * 
     * @param int $organisation_id
     * @param int $account_id
     * @param int $organisation_role_id
     * 
     * @return array
     */
    public function updateAccountRole($organisation_id, $account_id, $organisation_role_id) {
        $exists = $this->find('count', array(
            'conditions' => array(
                'OrganisationAccount.id' => $account_id,
                'OrganisationAccount.organisation_id' => $organisation_id
            )
        ));
        
        if( $exists ) {
            $this->updateAll(array(
                'OrganisationAccount.organisation_role_id' => $organisation_role_id
            ), array(
                'OrganisationAccount.organisation_id' => $organisation_id,
                'OrganisationAccount.id' => $account_id
            ));
            
            return array('status' => 'success', 'message' => 'Organisation account updated successfully.');
        }
        
        return array('status' => 'failed', 'message' => 'Account to update not found');
    }
}
